package com.xiaoyu.im.datamodel;

import android.text.TextUtils;
import android.util.SparseArray;

import in.srain.cube.concurrent.AppThreads;

import com.xiaoyu.base.entity.MessageEntity;
import com.xiaoyu.base.utils.FileUtil;
import com.xiaoyu.base.app.GlobalUI;

import org.jetbrains.annotations.NotNull;
import org.json.JSONArray;

import java.util.Collections;
import java.util.List;

import in.srain.cube.request.JsonData;
import in.srain.cube.util.CLog;
import in.srain.cube.util.internal.AppConsumer;

public class MessagePayload {

    public static final int CURRENT_VERSION = 2;
    public static final MessageEmptyAction EMPTY_ACTION = new MessageEmptyAction();

    public static final String KEY_NEW_TIP = "newTip";

    private static final String KEY_TYPE = "type";
    private static final String KEY_ATTRS = "attrs";
    private static final String KEY_TEXT = "text";
    private static final String KEY_URL = "url";
    private static final String KEY_FILE_PATH = "filePath";
    private static final String KEY_DURATION = "duration";
    private static final String KEY_MENTION_ALL = "mentionAll";
    private static final String KEY_MENTION_LIST = "mentionList";
    private static final String KEY_ACTION_CONFIG = "actionConfig";
    private static final SparseArray<VersionBridge> VERSION_BRIDGES = new SparseArray<>();

    private int mMessageType;
    private JsonData mAttrs;
    private String mText;
    private String mUrl;
    private String mFilePath;
    private int mDuration;
    private boolean mMentionAll;
    private List<String> mMentionList;
    private MessageActionWrapper mActionWrapper = new MessageActionWrapper(JsonData.newMap());

    static {
        VERSION_BRIDGES.put(1, new Version1Bridge());
        VERSION_BRIDGES.put(2, new Version2Bridge());
    }

    public static MessagePayload obtain(int type) {
        return new MessagePayload(type);
    }

    public static MessagePayload fromJsonData(JsonData jsonData) {
        final int type = jsonData.optInt(KEY_TYPE);
        final MessagePayload payload = obtain(type);
        payload.mUrl = jsonData.optString(KEY_URL);
        payload.mFilePath = jsonData.optString(KEY_FILE_PATH);
        payload.mText = jsonData.optString(KEY_TEXT);
        payload.mDuration = jsonData.optInt(KEY_DURATION);
        payload.mMentionAll = jsonData.optBoolean(KEY_MENTION_ALL);
        payload.mMentionList = jsonData.optJson(KEY_MENTION_LIST).asList();
        payload.mAttrs = jsonData.optJson(KEY_ATTRS);
        payload.mActionWrapper = new MessageActionWrapper(payload.mAttrs.optJson(KEY_ACTION_CONFIG));
        return payload;
    }

    public static MessagePayload fromEntity(MessageEntity entity) {
        final int version = entity.getPayloadVersion();
        final VersionBridge bridge = VERSION_BRIDGES.get(version);
        if (bridge == null) {
            throw new IllegalStateException(String.format("VersionBridge not found for version: %d", version));
        }
        return bridge.fromEntity(entity);
    }

    private MessagePayload(int type) {
        this.mMessageType = type;
    }

    public JsonData toJsonData() {
        final JsonData jsonData = JsonData.newMap();
        jsonData.put(KEY_TYPE, mMessageType);
        jsonData.put(KEY_URL, mUrl);
        jsonData.put(KEY_FILE_PATH, mFilePath);
        jsonData.put(KEY_TEXT, mText);
        jsonData.put(KEY_DURATION, mDuration);
        jsonData.put(KEY_MENTION_ALL, mMentionAll);
        if (mMentionList != null) {
            jsonData.put(KEY_MENTION_LIST, new JSONArray(mMentionList));
        }
        if (mAttrs != null) {
            jsonData.put(KEY_ATTRS, mAttrs.getRawData());
        }
        return jsonData;
    }

    public void recall(int messageType, String recallText, String originText) {
        setMessageType(messageType);
        setText(recallText);
        setUrl("");
        setDuration(0);
        setFilePath("");
        setMentionAll(false);
        setMentionList(Collections.emptyList());
        putAttrs("originText", originText);
    }

    public int getMessageType() {
        return mMessageType;
    }

    public void setMessageType(int type) {
        this.mMessageType = type;
    }

    public String getText() {
        return mText;
    }

    public void setText(String text) {
        this.mText = text;
    }

    public String getUrl() {
        return mUrl;
    }

    public void setUrl(String url) {
        this.mUrl = url;
    }

    public int getDuration() {
        return mDuration;
    }

    public void setDuration(int duration) {
        this.mDuration = duration;
    }

    public boolean isMentionAll() {
        return mMentionAll;
    }

    public void setMentionAll(boolean mentionAll) {
        this.mMentionAll = mentionAll;
    }

    public List<String> getMentionList() {
        return mMentionList;
    }

    public void setMentionList(List<String> mentionList) {
        this.mMentionList = mentionList;
    }

    public String getFilePath() {
        return mFilePath;
    }

    public void setFilePath(String filePath) {
        this.mFilePath = filePath;
    }

    public JsonData getAttrsCopy() {
        return mAttrs != null ? JsonData.create(mAttrs.toString()) : JsonData.newMap();
    }

    public void putAttrs(String key, Object value) {
        if (mAttrs == null) {
            mAttrs = JsonData.newMap();
        }
        mAttrs.put(key, value);
    }

    public String getLocalPathOrUrlForMedia() {
        if (!TextUtils.isEmpty(mFilePath) && FileUtil.isFileExist(mFilePath)) {
            return mFilePath;
        }
        if (!TextUtils.isEmpty(mUrl)) {
            return mUrl;
        }
        return "";
    }

    public boolean isInvisible(String uid) {
        final List<Object> visibleUids = mAttrs.optJson("visibleUids").asList();
        if (visibleUids.isEmpty()) {
            return false;
        }
        for (Object visibleUid : visibleUids) {
            if (TextUtils.equals(String.valueOf(visibleUid), uid)) {
                return false;
            }
        }
        return true;
    }

    public boolean isInvisibleLastContent(String uid) {
        final List<Object> visibleUids = mAttrs.optJson("visibleLastContentUids").asList();
        if (visibleUids.isEmpty()) {
            return false;
        }
        for (Object visibleUid : visibleUids) {
            if (TextUtils.equals(String.valueOf(visibleUid), uid)) {
                return false;
            }
        }
        return true;
    }

    public MessageActionWrapper getActionWrapper() {
        return mActionWrapper;
    }

    public int getCurrentVersion() {
        return CURRENT_VERSION;
    }

    @NotNull
    @Override
    public String toString() {
        return toJsonData().toString();
    }

    public static class MessageToastAction implements IMessageAction {

        private final String content;

        private MessageToastAction(JsonData jsonData) {
            this.content = jsonData.optString("content");
        }

        @Override
        public void execute() {
            AppThreads.runOnMainThread(() -> GlobalUI.getInstance().showToast(content));
        }

        @Override
        public JsonData toJson() {
            JsonData jsonData = JsonData.newMap();
            jsonData.put("content", content);
            return jsonData;
        }
    }

    public static class MessageRouteAction implements IMessageAction {

        private static final String TAG = MessageRouteAction.class.getSimpleName();
        private static AppConsumer<String> sExecutor;
        private final String url;

        public static void setExecutor(AppConsumer<String> executor) {
            sExecutor = executor;
        }

        private MessageRouteAction(JsonData jsonData) {
            this.url = jsonData.optString("url");
        }

        @Override
        public void execute() {
            if (sExecutor == null) {
                CLog.e(TAG, "execute() failure, cause consumer is null");
            } else {
                sExecutor.accept(url);
            }
        }

        @Override
        public JsonData toJson() {
            JsonData jsonData = JsonData.newMap();
            jsonData.put("url", url);
            return jsonData;
        }
    }

    public static class MessageAlertAction implements IMessageAction {

        private static final String TAG = MessageAlertAction.class.getSimpleName();

        public final String title;
        public final String message;
        public final String key;
        public final String payload;

        private static AppConsumer<MessageAlertAction> sExecutor;

        public static void setExecutor(AppConsumer<MessageAlertAction> executor) {
            sExecutor = executor;
        }

        private MessageAlertAction(JsonData jsonData) {
            title = jsonData.optString("title", null);
            message = jsonData.optString("message", null);
            key = jsonData.optString("key");
            payload = jsonData.optString("payload");
        }

        @Override
        public void execute() {
            if (sExecutor == null) {
                CLog.e(TAG, "execute() failure, cause consumer is null");
            } else {
                sExecutor.accept(this);
            }
        }

        @Override
        public JsonData toJson() {
            JsonData jsonData = JsonData.newMap();
            jsonData.put("title", title);
            jsonData.put("message", message);
            jsonData.put("key", key);
            jsonData.put("payload", payload);
            return jsonData;
        }
    }

    public static class MessageEmptyAction implements IMessageAction {
        @Override
        public void execute() {
        }

        @Override
        public JsonData toJson() {
            return JsonData.newMap();
        }
    }

    public static class MessageActionWrapper {
        private static final String KEY = "key";
        private static final String TYPE_TOAST = "toast";
        private static final String TYPE_ALERT = "alert";
        private static final String TYPE_ROUTE = "route";

        public final String key;
        public final IMessageAction action;

        private MessageActionWrapper(JsonData jsonData) {
            key = jsonData.optString("key");
            switch (key) {
                case TYPE_TOAST:
                    action = new MessageToastAction(jsonData.optJson(key));
                    break;
                case TYPE_ALERT:
                    action = new MessageAlertAction(jsonData.optJson(key));
                    break;
                case TYPE_ROUTE:
                    action = new MessageRouteAction(jsonData.optJson(key));
                    break;
                default:
                    action = EMPTY_ACTION;
                    break;
            }
        }

        public JsonData toJson() {
            JsonData jsonData = JsonData.newMap();
            jsonData.put(KEY, key);
            jsonData.put(key, action.toJson());
            return jsonData;
        }
    }

    private static class Version1Bridge implements VersionBridge {

        @Override
        public MessagePayload fromEntity(MessageEntity entity) {
            final JsonData jsonData = JsonData.create(entity.getContent());
            final JsonData fileJsonData = jsonData.optJson("_lcfile");
            final int type = jsonData.optInt("_lctype");
            final MessagePayload payload = obtain(type);
            payload.mText = jsonData.optString("_lctext");
            payload.mAttrs = jsonData.optJson("_lcattrs");
            payload.mFilePath = jsonData.optString("KEY_FILE_LOCAL_PATH");
            payload.mUrl = fileJsonData.optString("url");
            payload.mDuration = fileJsonData.optJson("metaData").optInt("duration");
            payload.mMentionAll = entity.isMentionAll();
            payload.mMentionList = JsonData.create(entity.getMentionListString()).asList();
            return payload;
        }
    }

    private static class Version2Bridge implements VersionBridge {

        @Override
        public MessagePayload fromEntity(MessageEntity entity) {
            return fromJsonData(JsonData.create(entity.getContent()));
        }
    }

    public interface IMessageAction {

        void execute();

        JsonData toJson();
    }

    private interface VersionBridge {

        MessagePayload fromEntity(MessageEntity entity);
    }
}
